package org.behrang.macbeans.tab;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.Icon;
import org.netbeans.swing.tabcontrol.plaf.AbstractTabCellRenderer;
import org.netbeans.swing.tabcontrol.plaf.TabPainter;
import org.openide.awt.HtmlRenderer;

/**
 * The base of all MacBeans tab cell renderers. Some methods are copy/pasted from
 * NetBeans' TabControl component's source code.
 * 
 * The only method implemented in this class is <code>paintIconAndText</code>.
 * 
 * @author Behrang Saeedzadeh
 */
public abstract class MacBeansTabCellRenderer extends AbstractTabCellRenderer {

    public MacBeansTabCellRenderer(TabPainter leftClip, TabPainter noClip, TabPainter rightClip, Dimension padding) {
        super(leftClip, noClip, rightClip, padding);
    }

    @Override
    public Insets getInsets() {
        return new Insets(2, 3, 5, 0);
    }
    
    /**
     * Paints tab's icon and text
     *
     * @param g The graphics context
     */
    @Override
    protected void paintIconAndText(Graphics g) {
        g.setFont(getFont());
        FontMetrics fm = g.getFontMetrics(getFont());

        //Find out what height we need
        int txtH = fm.getHeight();
        Insets ins = getInsets();

        //find out the available height
        int availH = getHeight() - (ins.top + ins.bottom);
        int txtY;
        if (availH > txtH) {            
            txtY = txtH + ins.top + ((availH / 2) - (txtH / 2)) - 3;
        } else {
            txtY = txtH + ins.top - 3;
        }
        int txtX;

        int centeringToAdd = getPixelsToAddToSelection() != 0 ? getPixelsToAddToSelection() / 2 : 0;

        Icon icon = getIcon();

        //Check the icon non-null and height (see TabData.NO_ICON for why)
        if (!isClipLeft() && icon != null && icon.getIconWidth() > 0 && icon.getIconHeight() > 0) {
            int iconY;
            if (availH > icon.getIconHeight()) {
                //add 2 to make sure icon top pixels are not cut off by outline
                iconY = ins.top + ((availH / 2) - (icon.getIconHeight() / 2)) + 1;
            } else {
                //add 2 to make sure icon top pixels are not cut off by outline
                iconY = ins.top + 1;
            }
            int iconX = ins.left + centeringToAdd;

            iconY += getIconYAdjustment();

            icon.paintIcon(this, g, iconX, iconY);
            txtX = iconX + icon.getIconWidth() + getIconTextGap();
        } else {
            txtX = ins.left + centeringToAdd;
        }

        if (icon != null && icon.getIconWidth() == 0) {
            //Add some spacing so the text isn't flush for, e.g., the
            //welcome screen tab
            txtX += 5;
        }

        txtY += getCaptionYAdjustment();
        
        //Get the available horizontal pixels for text
        int txtW = getWidth() - (txtX + ins.right) - 2;
        if (isClipLeft()) {
            //fiddle with the string to get "...blah"
            String s = preTruncateString(getText(), g, txtW - 4); //subtract 4 so it's not flush w/ tab edge

            HtmlRenderer.renderString(s, g, txtX, txtY + 1, txtW, txtH, getFont(),
                    new Color(226, 226, 228), HtmlRenderer.STYLE_TRUNCATE, true);
            
            HtmlRenderer.renderString(s, g, txtX, txtY, txtW, txtH, getFont(),
                    getForeground(), HtmlRenderer.STYLE_CLIP, true);
        } else {
            String s;
            if (isClipRight()) {
                //Jano wants to always show a "..." for cases where a tab is truncated,
                //even if we've really painted all the text.  
                s = getText() + "..."; //NOI18N

            } else {
                s = getText();
            }

            HtmlRenderer.renderString(s, g, txtX, txtY + 1, txtW, txtH, getFont(),
                    new Color(226, 226, 228), HtmlRenderer.STYLE_TRUNCATE, true);

            HtmlRenderer.renderString(s, g, txtX, txtY, txtW, txtH, getFont(),
                    getForeground(), HtmlRenderer.STYLE_TRUNCATE, true);

            
        }
    }

    static String preTruncateString(String s, Graphics g, int availPixels) {
        if (s.length() < 3) {
            return s;
        }
        s = stripHTML(s);
        if (s.length() < 2) {
            return "..." + s; //NOI18N

        }
        FontMetrics fm = g.getFontMetrics();
        int dotsWidth = fm.stringWidth("..."); //NOI18N

        int beginIndex = s.length() - 2;
        String test = s.substring(beginIndex);
        String result = test;
        while (fm.stringWidth(test) + dotsWidth < availPixels) {
            beginIndex--;
            if (beginIndex <= 0) {
                break;
            } else {
                result = test;
                test = s.substring(beginIndex);
            }
        }
        return "..." + result; //NOI18N

    }

    static boolean isHTML(String s) {
        boolean result = s.startsWith("<html>") || s.startsWith("<HTML>"); //NOI18N

        return result;
    }

    static String stripHTML(String s) {
        if (isHTML(s)) {
            StringBuffer result = new StringBuffer(s.length());
            char[] c = s.toCharArray();
            boolean inTag = false;
            for (int i = 0; i < c.length; i++) {
                //XXX need to handle entity includes
                boolean wasInTag = inTag;
                if (!inTag) {
                    if (c[i] == '<') {
                        inTag = true;
                    }
                } else {
                    if (c[i] == '>') {
                        inTag = false;
                    }
                }
                if (!inTag && wasInTag == inTag) {
                    result.append(c[i]);
                }
            }
            return result.toString();
        } else {
            return s;
        }
    }
}
